#include "LUNA_VorbisSound.h"
#include "LUNA_VorbisSystem.h"

using namespace LUNA;

VorbisSound::VorbisSound( const std::string &filename,
	VorbisSystem &vorbisSystem ) :
ISound( "VORBIS" ),
mVorbisSystem( vorbisSystem ),
mSource( 0 ),
mBuffer( 0 ),
mState( 0 ),
mLoop( 0 ),
mBuffersProcessed( 0 ),
mTotalBuffersProcessed( 0 ),
mQueuedBuffers( 0 ),
mFrequency( 0 ),
mFormat( 0 ),
mChannels( 0 ),
mBufferSize( 0 ),
mBytesWritten( 0 ),
mDecodeBuffer( 0 )
{
	FILE *oggFile = 0;
	fopen_s( &oggFile, filename.c_str(), "rb" );
	if( !oggFile )
	{
		throw std::runtime_error( "ogg file not found" );
	}
	
	vorbis_info *info = 0;
	
	if( mVorbisSystem.fn_ov_open_callbacks( oggFile, 
		&mOggVorbisFile, 0, 0, mVorbisSystem.vorbisCallbacks) == 0 )
	{
		info = mVorbisSystem.fn_ov_info( &mOggVorbisFile, -1 );
		mFrequency = info->rate;
		mChannels = info->channels;
		if( info->channels == 1 )
		{
			mFormat = AL_FORMAT_MONO16;
			mBufferSize = mFrequency >> 1;
			mBufferSize -= (mBufferSize % 2);
		}
		else if( info->channels == 2 )
		{
			mFormat = AL_FORMAT_STEREO16;
			mBufferSize = mFrequency;
			mBufferSize -= (mBufferSize % 4);
		}
		else if( info->channels == 4 )
		{
			mFormat = alGetEnumValue( "AL_FORMAT_QUAD16" );
			mBufferSize = mFrequency * 2;
			mBufferSize -= (mBufferSize % 8);
		}
		else if( info->channels == 6 )
		{
			mFormat = alGetEnumValue( "AL_FORMAT_51CHN16" );
			mBufferSize = mFrequency * 3;
			mBufferSize -= (mBufferSize % 12);
		}
		else
		{
			throw std::runtime_error( "unsupported number of channels" );
		}
	}
	else
	{
		int a = 0;
	}

	if( mFormat != 0 )
	{
		mDecodeBuffer = (char*)malloc( mBufferSize );
		if( !mDecodeBuffer )
		{
			mVorbisSystem.fn_ov_clear( &mOggVorbisFile );
			throw std::runtime_error( "failed to allocate vorbis buffer" );
		}

		alGenBuffers( NUMBUFFERS, mBuffers );

		alGenSources( 1, &mSource );

		for( mLoop = 0; mLoop < NUMBUFFERS; ++mLoop )
		{
			mBytesWritten = 
				mVorbisSystem.decodeOggVorbis( 
					&mOggVorbisFile, 
					mDecodeBuffer, 
					mBufferSize, 
					mChannels 
					);
			if( mBytesWritten )
			{
				alBufferData( 
					mBuffers[mLoop], 
					mFormat,
					mDecodeBuffer,
					mBytesWritten,
					mFrequency
					);
				alSourceQueueBuffers( mSource, 1, &mBuffers[mLoop] );
			}
		}
	}
	else
	{
		throw std::runtime_error( "no format for ogg" );
	}
	
	mTotalBuffersProcessed = 0;
}

VorbisSound::~VorbisSound()
{
}

void VorbisSound::update()
{
	mBuffersProcessed = 0;
	alGetSourcei( mSource, AL_BUFFERS_PROCESSED, &mBuffersProcessed );

	// example shows this as being for "logging purposes"
	mTotalBuffersProcessed += mBuffersProcessed;

 	while( mBuffersProcessed )
	{
		mBuffer = 0;
		alSourceUnqueueBuffers( mSource, 1, &mBuffer );

		mBytesWritten = 
			mVorbisSystem.decodeOggVorbis( 
				&mOggVorbisFile, 
				mDecodeBuffer, 
				mBufferSize, 
				mChannels 
				);
		if( mBytesWritten )
		{
			alBufferData( 
				mBuffer, 
				mFormat, 
				mDecodeBuffer, 
				mBytesWritten, 
				mFrequency 
				);
			alSourceQueueBuffers( mSource, 1, &mBuffer );
		}

		--mBuffersProcessed;
	}

	alGetSourcei( mSource, AL_SOURCE_STATE, &mState );
	if( mState != AL_PLAYING )
	{
		alGetSourcei( mSource, AL_BUFFERS_QUEUED, &mQueuedBuffers );
		if( mQueuedBuffers )
		{
			alSourcePlay( mSource );
		}
		else
		{
			int a = 0;
		}
	}
}

void VorbisSound::setPlayState( SoundPlayState state )
{
	switch( state )
	{
		case SPS_PLAY:
			alSourcePlay( mSource );
			break;
		case SPS_PAUSE:
			alSourcePause( mSource );
			break;
		case SPS_STOP:
			alSourceStop( mSource );
			break;
		default:
			THROW_RT("bad play state");
			break;
	}
}

SoundPlayState VorbisSound::getPlayState() const
{
	ALint state = 0;
	alGetSourcei( mSource, AL_SOURCE_STATE, &state );

	switch( state )
	{
	case AL_INITIAL:
	case AL_STOPPED:
		return SPS_STOP;
		break;
	case AL_PAUSED:
		return SPS_PAUSE;
		break;
	case AL_PLAYING:
		return SPS_PLAY;
		break;
	}

	return SPS_NULL;
}

void VorbisSound::setVolume( float volume )
{
	float v = volume * getMasterVolume();
	alSourcef( mSource, AL_GAIN, v );
}

void VorbisSound::_setMasterVolume( float volume )
{
		mMasterVolume = volume;	
	alSourcef( mSource, AL_GAIN, volume * getMasterVolume() );
}